/**
 * doubly linkedlist
 * has to support the following operation efficiently<br>
 * 1. the entry with minimum time (nearestEvent pointer)<br>
 * 2. search and update multiple entries with the same oid of mo_from or mo_to <br>
 * 3.  add new entry <br>
 * 4. remove nearest  
 * @author Xiaohui
 * 
 */
package entity;

import java.util.HashMap;
import java.util.LinkedList;

/**
 *
 * 
 * @author Xiaohui
 * 
 */
public class MyEventQ {
	MyEvent nearestEvent;
	MyEvent first;
	MyEvent last;
	int size;

	public MyEventQ() {
		nearestEvent = null;
		first = null;
		last = null;
		size = 0;
	}

	/**
	 * batch update, scan the list once
	 * 
	 * @param moid
	 * @param moid2OccTime
	 * @param from
	 */
	public void batUpdate(int moid, HashMap<Integer, Double> moid2OccTime,
			boolean from) {
		MyEvent e = first;
		if (from) {
			// check first entry
			if (e.mo_from.oid == moid
					&& moid2OccTime.keySet().contains(e.mo_to.oid)) {
				double newOccTime = moid2OccTime.get(e.mo_to.oid);
				e.timeToHappen = newOccTime;
				if (newOccTime < nearestEvent.timeToHappen) {
					nearestEvent = e;
				}
			} else {
				if (e.mo_to.oid == moid
						&& moid2OccTime.keySet().contains(e.mo_from.oid)) {
					double newOccTime = moid2OccTime.get(e.mo_from.oid);
					e.timeToHappen = newOccTime;
					if (newOccTime < nearestEvent.timeToHappen) {
						nearestEvent = e;
					}
				}
			}
			e = e.next;
			while (!e.equals(last)) {
				if (e.mo_from.oid == moid
						&& moid2OccTime.keySet().contains(e.mo_to.oid)) {
					double newOccTime = moid2OccTime.get(e.mo_to.oid);
					e.timeToHappen = newOccTime;
					if (newOccTime < nearestEvent.timeToHappen) {
						nearestEvent = e;
					}
				}
				e = e.next;
			}
		} else {
			while (!e.equals(first)) {
				if (e.mo_to.oid == moid
						&& moid2OccTime.keySet().contains(e.mo_from.oid)) {
					double newOccTime = moid2OccTime.get(e.mo_from.oid);
					e.timeToHappen = newOccTime;
					if (newOccTime < nearestEvent.timeToHappen) {
						nearestEvent = e;
					}
				}
				e = e.next;
			}
		}
		// udpate size
		size += moid2OccTime.size();
	}

	public void clear() {
		nearestEvent = null;
		first = null;
		last = null;
		size = 0;
	}

	/**
	 * find second nearest event <br>
	 * 
	 * set nearest pointer to second largest<br>
	 * remove the nearest event <br>
	 * 
	 * @return the nearest event
	 */
	public void delNearest() {
		double occTime;
		MyEvent secondNearest;
		MyEvent e;
		if (first.equals(last)) {
			this.clear();
			return;
		}

		if (!nearestEvent.equals(first)) {
			occTime = first.timeToHappen;
			secondNearest = first;
		} else {
			occTime = first.next.timeToHappen;
			secondNearest = first.next;
		}

		e = first;
		while (!e.equals(last)) {
			if (e.timeToHappen < occTime
					&& e.timeToHappen > nearestEvent.timeToHappen) {
				secondNearest = e;
				occTime = e.timeToHappen;
			}
			e = e.next;
		}
		// remove nearest
		nearestEvent.previous.next = nearestEvent.next;
		nearestEvent.next.previous = nearestEvent.previous;
		// point to right node
		nearestEvent = secondNearest;
		// update counter
		size--;
	}

	public String toString() {
		String ret = "";
		MyEvent current = first; // start at beginning
		while (current != null) // until end of list,
		{
			ret += current.toString() + ", ";
			current = current.next; // move to next link
		}
		return ret;
	}

	public MyEvent getFirst() {
		return this.first;

	}

	public MyEvent getLast() {
		return this.last;
	}

	public MyEvent getNearEvent() {
		return this.nearestEvent;
	}

	/**
	 * Always add behind
	 * 
	 * @param newEvent
	 */
	public void insertFirst(MyEvent newEvent) {
		if (isEmpty()) {
			nearestEvent = newEvent;
			last = newEvent;
		} else {
			if (newEvent.timeToHappen < nearestEvent.timeToHappen) {
				nearestEvent = newEvent;
			}
			first.previous = newEvent;
		}
		newEvent.next = first;
		first = newEvent;

		// update counter
		size++;
	}

	public void insertLast(MyEvent newEvent) {
		if (isEmpty()) {
			first = newEvent;
			nearestEvent = newEvent;
		} else {
			last.next = newEvent;
			newEvent.previous = last;
			if (nearestEvent.timeToHappen > newEvent.timeToHappen) {
				nearestEvent = newEvent;
			}
		}
		last = newEvent;

		// udpate size
		size++;
	}

	public boolean isEmpty() {
		return first == null;
	}

	public int size() {
		return this.size;
	}

	/**
	 * update relevant entries
	 * 
	 * @param from_moid
	 * @param to_moid
	 * @param newOccTime
	 */
	public void update(int from_moid, int to_moid, double newOccTime) {
		// check first entry
		MyEvent e = first;
		if (e.mo_from.oid == from_moid && e.mo_to.oid == to_moid) {
			e.timeToHappen = newOccTime;
			if (newOccTime < nearestEvent.timeToHappen) {
				nearestEvent = e;
			}
			return;
		}
		e = e.next;
		while (!e.equals(first)) {
			if (e.mo_from.oid == from_moid && e.mo_to.oid == to_moid) {
				e.timeToHappen = newOccTime;
				if (newOccTime < nearestEvent.timeToHappen) {
					nearestEvent = e;
				}
				e = e.next;
			}
		}
	}

}
